// System includes
#include <QDebug>

// Local includes
#include "yig.h"
#include "graphics/yigcolorkit.h"
#include "stdio.h"
#include "gui/yiggraphicsscene.h"

//QString USER_NAME = "";
//bool ONLINE = false;

QString USER_NAME = "";
bool ONLINE = false;

Yig::Yig(bool onlineBool, YigClient *yigClient, SCLang* scLang, QString appDirectory, QSemaphore *wait4CleanupDone) :
    mYigClient(yigClient),
    wait4CleanupDone(wait4CleanupDone),
    mOSCthulhuPort(loginInfo.oscthulhuPort),
    mAppDirectory(appDirectory),
    packet( packetBuffer, PACKET_BUFFER_SIZE ),
    yigChat(this),
    scoreWindow(0)
{
    YigSynthGraphic::initCircleImage();
    USER_NAME = loginInfo.userName;
    ONLINE = onlineBool;

    mSCLang = scLang;
    startUpTimer.setSingleShot(true);
    startUpTimer.setInterval(2000);
    QObject::connect(mSCLang, SIGNAL(finishedBooting()), this, SLOT(createSynthMenu()));
    QObject::connect(this, SIGNAL(quitting()), mSCLang, SLOT(handleQuit()));
    QObject::connect(&mainWindow, SIGNAL(recording(bool)), mSCLang, SLOT(record(bool)));
    QObject::connect(&mainWindow, SIGNAL(networking(bool)), this, SLOT(networking(bool)));

    // Populate the channelList with initialized YigNetworkChannels and connect signals/slots with the channelView
    //for(int i = 0; i < NUMBER_OF_NETWORK_CHANNELS; i++)
    //{
        //networkChannel = new YigNetworkChannel(0, this, scLang);
        networkChannel = new YigNetworkChannel(0, this);
        YigNetworkChannelView* networkChannelView = mainWindow.getGraphicsView();

        QObject::connect(mSCLang, SIGNAL(finishedBooting()), networkChannel, SLOT(createGroups()));

        QObject::connect(&startUpTimer, SIGNAL(timeout()), networkChannelView, SLOT(autoCableAllSynths()));

        QObject::connect(networkChannelView, SIGNAL(dragAndDrop(QString,QString,QPoint,float,float)),
                         networkChannel, SLOT(requestCreateSynth(QString,QString,QPoint,float,float)));

        //QObject::connect(networkChannelView, SIGNAL(dragAndDrop(QString,QString,QPoint)),
        //                 networkChannel, SLOT(requestCreateSynth(QString,QString,QPoint)));

        QObject::connect(networkChannel, SIGNAL(synthCreated(YigSynth*,QString,QString,QPoint,float,float)),
                         networkChannelView, SLOT(createSynthGraphic(YigSynth*,QString,QString,QPoint,float,float)));

        QObject::connect(networkChannelView, SIGNAL(keyboardDeleteSynth(QString)),
                         networkChannel, SLOT(requestDestroySynth(QString)));

        //QObject::connect(networkChannelView, SIGNAL(keyboardDeleteSynth(QString)),
        //                 networkChannel, SLOT(requestDestroySynth(QString)));

        QObject::connect(networkChannel, SIGNAL(synthDestroyed(QString)),
                         networkChannelView, SLOT(destroySynthGraphic(QString)));

        QObject::connect(networkChannelView, SIGNAL(synthParamChanged(QString,YigSynthGraphic::ParamType,float)),
                         this, SLOT(updateSynthParam(QString,YigSynthGraphic::ParamType,float)));

        QObject::connect(networkChannelView, SIGNAL(synthPositionChanged(QString,QPointF)),
                         this, SLOT(updateSynthPosition(QString,QPointF)));

        QObject::connect(networkChannelView, SIGNAL(cableCreated(QString,QString,QString,YigCableGraphic::CableOutputType)),
                         networkChannel, SLOT(createCableObject(QString,QString,QString,YigCableGraphic::CableOutputType)));

        QObject::connect(networkChannelView, SIGNAL(cableDestroyed(QString)), networkChannel, SLOT(destroyCableObject(QString)));

        QObject::connect(((YigGraphicsScene*) networkChannelView->scene()), SIGNAL(requestAudioBuffers(QHash<int,SndBuf*>*)),
                         mSCLang, SLOT(requestAudioBuffers(QHash<int,SndBuf*>*)));

        //QObject::connect(((YigGraphicsScene*) networkChannelView->scene()), SIGNAL(requestAudioBuffers(QHash<int,AudioBuffer*>*)),
        //                 mSCLang->getServer()->getServerThread()->getSCSynth(), SLOT(requestAudioBuffers(QHash<int,AudioBuffer*>*)),
        //                 Qt::QueuedConnection);

        QObject::connect(networkChannelView, SIGNAL(cursorPositionChanged(QPoint)),
                         this, SLOT(updateCursorPosition(QPoint)), Qt::QueuedConnection);

        QObject::connect(networkChannelView, SIGNAL(synthSelectedChanged(QString,int)), this, SLOT(synthSelectedChanged(QString,int)));


        ///////////////////////////
        // Yig Options Connections
        QObject::connect(&mainWindow, SIGNAL(toggleGraphicsAnimation(bool)),
                         ((YigGraphicsScene*) networkChannelView->scene()), SLOT(toggleGraphicsAnimation(bool)));

        // Audio Piping
        QObject::connect(&mainWindow, SIGNAL(toggleAudioPipe(bool,QString,int)),
                         mSCLang, SLOT(toggleAudioPipe(bool,QString,int)));

        //QObject::connect(&mainWindow, SIGNAL(toggleAudioPipe(bool,QString,int)),
        //                 mSCLang->getServer()->getServerThread(), SLOT(toggleAudioPipe(bool,QString,int)));

        //QObject::connect(((YigGraphicsScene*) networkChannelView->scene()), SIGNAL(requestAudioBuffer(int,AudioBuffer*)),
        //                 mSCLang->getServer()->getServerThread()->getSCSynth(), SLOT(requestAudioBuffer(int,AudioBuffer*)),
        //                 Qt::QueuedConnection);

        //QObject::connect(mainWindow.glWidget.getGLThread(), SIGNAL(requestAudioBuffers(QHash<int,AudioBuffer>*)),
        //                 mSCLang->getServer()->getServerThread()->getSCSynth(), SLOT(requestAudioBuffers(QHash<int,AudioBuffer>*)));

        //((YigGraphicsScene*) networkChannelView->scene())->setServerOptions(mSCLang->getServer()->options);
        //mainWindow.glWidget.getGLThread()->setServerOptions(mSCLang->getServer()->options);
    //    channelList.append(networkChannel);
    //}

    // Set channel 0 as the active network channel view, which effects drag/droppable status and interactivity
    //setActiveChannel(0);
    scLang->bootServer();
}

Yig::~Yig()
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage("/removeSyncObject") << qPrintable(QString(USER_NAME+"Cursor")) << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);

    /*
    while(!channelList.isEmpty())
    {
        delete channelList.takeFirst();
    }
    channelList.clear();*/
    delete networkChannel;
    delete synthMenu;
    if(YigSynthGraphic::staticImageInitBool)
    {
        delete YigSynthGraphic::circleImage;
        YigSynthGraphic::staticImageInitBool = false;
    }

    if(scoreWindow != 0)
    {
        delete scoreWindow;
    }
}

void Yig::setActiveChannel(int channelNum)
{
    currentUserChannel = channelNum;
    mainWindow.setActiveChannel(channelNum);
}

void Yig::sendCthulhu(const char *command)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage(command) << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::sendCthulhu(const char *command, const char *value)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage(command) << value << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::sendCthulhu(const char *command, int value)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage(command) << value << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::sendCthulhu(const char *command, float value)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage(command) << value << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::requestCreateSynth(const char *mapKey, const char *synthName, int xPos, int yPos, float param1, float param2)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage("/addSyncObject") << mapKey << "Yig" << "SynthObject" << synthName
      << xPos << yPos << param1 << param2 << " " << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::requestDeleteSynth(const char *mapKey)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage("/removeSyncObject") << mapKey << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::updateSynthPosition(QString mapKey, QPointF point)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage("/setSyncArg") << qPrintable(mapKey)  << 1 << (int) point.x() << 1 << osc::EndMessage;

    mYigClient->send(p, mOSCthulhuPort);

    p.Clear();
    p << osc::BeginMessage("/setSyncArg") << qPrintable(mapKey)  << 2 << (int) point.y() << 1 << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::updateSynthParam1(const char *mapKey, float value)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage("/setSyncArg") << mapKey  << 3 << value << 1 << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::updateSynthParam2(const char *mapKey, float value)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage("/setSyncArg") << mapKey << 4 << value << 1 << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::updateSynthParam(QString mapKey, YigSynthGraphic::ParamType paramType, float value)
{
    QByteArray ba = mapKey.toLocal8Bit();

    if(paramType == YigSynthGraphic::Param1)
    {
        updateSynthParam1(ba.data(), value);
    }

    else
    {
        updateSynthParam2(ba.data(), value);
    }
}

void Yig::synthSelectedChanged(QString mapKey, int selected)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    if(selected == 1)
    {
        p << osc::BeginMessage("/setSyncArg") << qPrintable(mapKey) << 5 << qPrintable(USER_NAME) << 1 << osc::EndMessage;
    }

    else
    {
        p << osc::BeginMessage("/setSyncArg") << qPrintable(mapKey) << 5 << " " << 1 << osc::EndMessage;
    }

    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::updateCursorPosition(QPoint pos)
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage("/setSyncArg") << qPrintable(QString(USER_NAME+"Cursor"))  << 0 << (int) pos.x() << 1 << osc::EndMessage;

    mYigClient->send(p, mOSCthulhuPort);

    p.Clear();
    p << osc::BeginMessage("/setSyncArg") << qPrintable(QString(USER_NAME+"Cursor"))  << 1 << (int) pos.y() << 1 << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::setupNetworkConnections(YigPacketListener *listener)
{

    YigNetworkChannelView* view = mainWindow.getGraphicsView();

    // Server updates
    QObject::connect(listener, SIGNAL(serverStatus(QString,int,float,float)),
                     &mainWindow, SLOT(updateServerStatus(QString,int,float,float)), Qt::QueuedConnection);

    // Chat updates
    QObject::connect(listener, SIGNAL(chatMessage(QString)), &yigChat, SLOT(receiveMessage(QString)), Qt::QueuedConnection);

    // Add Synth
    QObject::connect(listener, SIGNAL(addSynth(QString,QString,QPoint,float,float)),
                     networkChannel, SLOT(createSynthObject(QString,QString,QPoint,float,float)));

    // Remove Synth
    QObject::connect(listener, SIGNAL(removeSynth(QString)), networkChannel, SLOT(destroySynthObject(QString)));

    // Sync Synth Name (check synth alive)
    //QObject::connect(listener, SIGNAL(synthNameSync(QString,QString)), networkChannel, SLOT(checkSynthAlive(QString,QString)));

    // Sync Synth param1
    QObject::connect(listener, SIGNAL(updateSynthParam1(QString,float)), networkChannel, SLOT(updateSynthParam1(QString,float)));
    QObject::connect(listener, SIGNAL(updateSynthParam1(QString,float)), view, SLOT(updateSynthParam1(QString,float)));

    // Sync Synth Param2
    QObject::connect(listener, SIGNAL(updateSynthParam2(QString,float)), networkChannel, SLOT(updateSynthParam2(QString,float)));
    QObject::connect(listener, SIGNAL(updateSynthParam2(QString,float)), view, SLOT(updateSynthParam2(QString,float)));

    // Update Synth xPos
    QObject::connect(listener, SIGNAL(updateSynthPositionX(QString,int)), view, SLOT(updateSynthPositionX(QString,int)));

    // Update Synth yPos
    QObject::connect(listener, SIGNAL(updateSynthPositionY(QString,int)), view, SLOT(updateSynthPositionY(QString,int)));

    // Update Synth selected status
    QObject::connect(listener, SIGNAL(updateSynthSelected(QString,QString)), view, SLOT(updateSynthSelected(QString,QString)));

    // Add Peer
    QObject::connect(listener, SIGNAL(addPeer(QString)), &mainWindow, SLOT(addUser(QString)));
    QObject::connect(listener, SIGNAL(addPeer(QString)), view, SLOT(addUser(QString)));

    // Remove peer
    QObject::connect(listener, SIGNAL(removePeer(QString)), &mainWindow, SLOT(removeUser(QString)));
    QObject::connect(listener, SIGNAL(removePeer(QString)), view, SLOT(removeUser(QString)));

    // Update Cursor Position
    QObject::connect(listener, SIGNAL(updateCursorPositionX(QString,int)), view, SLOT(updateCursorPositionX(QString,int)));
    QObject::connect(listener, SIGNAL(updateCursorPositionY(QString,int)), view, SLOT(updateCursorPositionY(QString,int)));

    // Username update
    QObject::connect(listener, SIGNAL(userNameReply()), this, SLOT(createCursorObject()));
}

void Yig::createSynthMenu()
{
    synthMenu = new YigSynthMenu(mAppDirectory);
    // Set the synth menu location
    synthMenu->move(mainWindow.width() - synthMenu->width() - 20, 50);
    synthMenu->show();

    scoreWindow = new YigScore;
    scoreWindow->move(mainWindow.width() - scoreWindow->width() - 20, mainWindow.height() - scoreWindow->height() + 2);
    scoreWindow->show();

    yigChat.moveElements(mainWindow.width() - 410, mainWindow.height() - 280);
    mYigClient->start();
    bootMenu.close();
}

void Yig::handleQuit()
{
    if(ONLINE)
    {
        endOSCthulhuNetworking();
    }
    // so we can finish cleaning up in other classes
    //ONLINE = true;
    emit quitting();
    //oscthulhuClient.stop();
    qDebug() << "Yig Client is shutting down...";
    wait4CleanupDone->release();
}

void Yig::finishQuit()
{
    //qDebug() << "Yig Client is shutting down...";
    //wait4CleanupDone->release();
}

void Yig::networking(bool networking)
{
    if(networking)
    {
        beginOSCthulhuNetworking();
    }

    else
    {
        endOSCthulhuNetworking();
    }
}

void Yig::createCursorObject()
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage("/addSyncObject") << qPrintable(QString(USER_NAME+"Cursor")) << "Yig" << "CursorObject"
      << 0 << 0 << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
}

void Yig::beginOSCthulhuNetworking()
{
    //createLogInMenu();
    mainWindow.getGraphicsView()->clearSynths();
    ONLINE = true;
    sendCthulhu("/changePorts", 32244);
    sendCthulhu("/chat", "Yig client booting");
    yigChat.showElements();
    login();
    startUpTimer.start();
    synthMenu->show();
    synthMenu->raise();

    scoreWindow->show();
    scoreWindow->raise();
    scoreWindow->move(mainWindow.width() - scoreWindow->width() - 410 - 3, mainWindow.height() - scoreWindow->height() + 2);
}

void Yig::endOSCthulhuNetworking()
{
    char buffer[PACKET_BUFFER_SIZE];
    osc::OutboundPacketStream p( buffer, PACKET_BUFFER_SIZE );
    p << osc::BeginMessage("/removeSyncObject") << qPrintable(QString(USER_NAME+"Cursor")) << osc::EndMessage;
    mYigClient->send(p, mOSCthulhuPort);
    sendCthulhu("/cleanup", "Yig");
    ONLINE = false;
    yigChat.hideElements();
    //destroyLogInMenu();
    synthMenu->show();
    synthMenu->raise();
    scoreWindow->move(mainWindow.width() - scoreWindow->width() - 20, mainWindow.height() - scoreWindow->height() + 2);
}

void Yig::login()
{
    sendCthulhu("/login", "Yig");
}
